﻿using System.Collections.Concurrent;

namespace YunQue.Gateway
{
    /// <summary>
    /// 粘性会话中间件
    /// </summary>
    public class StickySessionMiddleware
    {
        private readonly RequestDelegate _next;
        private MemoryCacheHelper _cacheHelper = MemoryCacheHelper.GetInstance();

        public StickySessionMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            //前端请求的地址格式 /api/服务标识/接口 如 /api/tenantapi/login/login
            //根据tenantapi 获取服务的信息
            var pathArr = context.Request.Path.Value.Split('/');
            var serviceIdentifier = pathArr.Length > 3 ? pathArr[2] : string.Empty;
            if (IsStickySessionEnabled(serviceIdentifier))
            {
                // 在这里可以根据需要获取客户端的标识，比如 IP 地址或会话 ID
                string clientIdentifier = context.Connection.RemoteIpAddress.ToString();
                // 根据客户端标识查找映射表，确定要路由到的后端服务
                string backendService = GetBackendServiceForClient(clientIdentifier,serviceIdentifier);
                // 修改请求的目标地址
                context.Request.Path = context.Request.Path.Value.Replace(serviceIdentifier, backendService);
            }
            await _next(context);
        }
        /// <summary>
        /// 判断是否启用粘性会话
        /// </summary>
        /// <param name="serviceIdentifier"></param>
        /// <returns></returns>
        private bool IsStickySessionEnabled(string serviceIdentifier)
        {
            // 在这里编写判断是否启用粘性会话的逻辑，比如从配置文件或数据库中读取
            // 这里简单地返回 true 作为示例
            return serviceIdentifier == "tenantapi_s";
        }
        /// <summary>
        /// 后去后端服务名称
        /// </summary>
        /// <param name="clientIdentifier"></param>
        /// <param name="backendService"></param>
        /// <returns></returns>
        private string GetBackendServiceForClient(string clientIdentifier, string backendService)
        {
            return _cacheHelper.GetOrCreate(clientIdentifier, entry =>
            {
                // 需要根据服务表示获取注册的服务数量，然后在进行分发
                // 这里简单地使用随机数来模拟后端服务的选择
                Random random = new Random();
                int num = random.Next(0, 2);
                return $"{backendService}{num}";
            }, TimeSpan.FromMinutes(30));
        }
    }
}
